package com.yeskery.nut.extend.mcp.server.method;

import com.yeskery.nut.bean.ApplicationContext;
import com.yeskery.nut.bind.BindObject;
import com.yeskery.nut.bind.FitValueHelper;
import com.yeskery.nut.extend.mcp.core.*;
import com.yeskery.nut.extend.mcp.core.schema.ContentSchema;
import com.yeskery.nut.extend.mcp.server.McpException;
import com.yeskery.nut.extend.mcp.server.McpExecuteMetadata;
import com.yeskery.nut.extend.mcp.server.McpServerCapability;
import com.yeskery.nut.extend.mcp.server.McpServerSession;
import com.yeskery.nut.util.BeanUtils;
import com.yeskery.nut.util.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 基础调用方法处理器
 * @author sprout
 * 2025-05-06 10:34
 */
public abstract class BaseCallMethodHandler extends BaseServerMethodHandler {
    /**
     * 构造工具调用方法处理器
     * @param applicationContext  应用上下文
     * @param mcpServerCapability 服务能力
     */
    public BaseCallMethodHandler(ApplicationContext applicationContext, McpServerCapability mcpServerCapability) {
        super(applicationContext, mcpServerCapability);
    }

    @Override
    protected void handleJsonResponse(McpServerSession serverSession, JsonMessage jsonMessage, JsonResponse jsonResponse) {
        Object callRequest = getCallRequest(jsonMessage);
        Map<String, Object> arguments = getCallRequestArguments(callRequest);
        McpExecuteMetadata callRequestMetadata = getCallRequestMetadata(callRequest);
        Method method = callRequestMetadata.getMethod();
        Parameter[] parameters = method.getParameters();
        FitValueHelper fitValueHelper = FitValueHelper.getInstance();
        Object[] parameterValues = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            String name = getParameterName(parameter);
            boolean required = isParameterRequired(parameter);
            Object value = arguments.get(name);
            if (required && value == null) {
                throw new McpException(new JsonError(Constants.INVALID_PARAMS, "Parameter [" + name + "] is required."));
            }
            if (value == null) {
                parameterValues[i] = null;
            } else {
                BindObject bindObject = fitValueHelper.getFitParamValue(name, value.toString(), parameter.getType());
                if (bindObject.isEmpty() && required) {
                    throw new McpException(new JsonError(Constants.INVALID_PARAMS, "Parameter [" + name + "] is required."));
                }
                parameterValues[i] = bindObject.getData();
            }
        }
        try {
            Object invokeResult = method.invoke(callRequestMetadata.getBean(), parameterValues);
            handleResponse(callRequestMetadata, callRequest, method.getGenericReturnType(), invokeResult, jsonResponse);
        } catch (Exception e) {
            ExecuteExceptionResult result = getExecuteExceptionMessage(e, callRequestMetadata, callRequest);
            handleErrorResponse(result, jsonResponse);
        }
    }

    /**
     * 处理错误响应
     * @param executeExceptionResult 执行异常结果
     * @param jsonResponse json响应
     */
    protected void handleErrorResponse(ExecuteExceptionResult executeExceptionResult, JsonResponse jsonResponse) {
        throw new McpException(new JsonError(Constants.INTERNAL_ERROR, executeExceptionResult.getMessage()));
    }

    /**
     * 获取调用请求
     * @param jsonMessage json消息
     * @return 调用请求
     */
    protected abstract Object getCallRequest(JsonMessage jsonMessage);

    /**
     * 获取调用请求参数
     * @param request 调用请求
     * @return 调用请求参数
     */
    protected abstract Map<String, Object> getCallRequestArguments(Object request);

    /**
     * 获取调用请求元数据
     * @param request 调用请求
     * @return 调用请求元数据
     */
    protected abstract McpExecuteMetadata getCallRequestMetadata(Object request);

    /**
     * 获取参数名称
     * @param parameter 参数
     * @return 参数名称
     */
    protected abstract String getParameterName(Parameter parameter);

    /**
     * 判断参数是否必填
     * @param parameter 参数
     * @return 是否必填
     */
    protected abstract boolean isParameterRequired(Parameter parameter);

    /**
     * 处理响应
     * @param callRequestMetadata 工具元数据
     * @param callRequest 请求
     * @param returnType 返回类型
     * @param invokeResult 调用结果
     * @param jsonResponse json响应
     */
    protected abstract void handleResponse(McpExecuteMetadata callRequestMetadata, Object callRequest, Type returnType, Object invokeResult, JsonResponse jsonResponse);

    /**
     * 获取异常处理器
     * @param callRequestMetadata 工具元数据
     * @param callRequest 请求
     * @return 异常处理器
     */
    protected abstract ExecuteExceptionHandler getExecuteExceptionHandler(McpExecuteMetadata callRequestMetadata, Object callRequest);

    /**
     * 获取默认异常信息
     * @param e 异常
     * @param callRequestMetadata 工具元数据
     * @param callRequest 请求
     * @return 异常信息
     */
    protected String getDefaultExceptionMessage(Exception e, McpExecuteMetadata callRequestMetadata, Object callRequest) {
        return "Mcp Method Invoke Fail.";
    }

    /**
     * 获取调用结果内容
     * @param returnType 返回类型
     * @param invokeResult 调用结果
     * @return 调用结果内容
     */
    protected List<ContentSchema.Content> getInvokeResultContent(Type returnType, Object invokeResult) {
        List<ContentSchema.Content> contents = new ArrayList<>();
        if(BeanUtils.isArrayDependBean(returnType)) {
            for (Object object : (Object[]) invokeResult) {
                contents.add(parseContent(object, BeanUtils.getDependBeanClass(returnType)));
            }
        } else if (BeanUtils.isCollectionDependBean(returnType)) {
            for (Object object : (Iterable<?>) invokeResult) {
                contents.add(parseContent(object, BeanUtils.getDependBeanClass(returnType)));
            }
        } else {
            contents.add(parseContent(invokeResult, BeanUtils.getDependBeanClass(returnType)));
        }
        return contents;
    }

    /**
     * 获取异常信息
     * @param e 异常
     * @param callRequestMetadata 工具元数据
     * @param callRequest 请求
     * @return 异常结果
     */
    private ExecuteExceptionResult getExecuteExceptionMessage(Exception e, McpExecuteMetadata callRequestMetadata, Object callRequest) {
        ExecuteExceptionHandler exceptionHandler = getExecuteExceptionHandler(callRequestMetadata, callRequest);
        if (exceptionHandler == null) {
            ExecuteExceptionResult result = new ExecuteExceptionResult();
            result.setIsError(true);
            result.setMessage(getDefaultExceptionMessage(e, callRequestMetadata, callRequest));
            return result;
        }
        Throwable exception;
        if (e instanceof InvocationTargetException) {
            exception = ((InvocationTargetException) e).getTargetException();
        } else {
            exception = e;
        }
        ExecuteExceptionResult result = exceptionHandler.handle(exception);
        if (result == null) {
            result = new ExecuteExceptionResult();
            result.setIsError(true);
            result.setMessage(getDefaultExceptionMessage(e, callRequestMetadata, callRequest));
            return result;
        }
        if (StringUtils.isBlank(result.getMessage()) && (result.getContents() == null || result.getContents().isEmpty())) {
            result.setMessage(getDefaultExceptionMessage(e, callRequestMetadata, callRequest));
        }
        return result;
    }

    /**
     * 解析返回内容
     * @param object 返回结果
     * @param returnClass 返回类型
     * @return 返回内容
     */
    private ContentSchema.Content parseContent(Object object, Class<?> returnClass) {
        if (ContentSchema.Content.class.isAssignableFrom(returnClass)) {
            return (ContentSchema.Content) object;
        }
        ContentSchema.TextContent textContent = new ContentSchema.TextContent();
        textContent.setText(object == null
                ? "none" : FitValueHelper.getInstance().isBasicTypeClass(object.getClass())
                ? object.toString() : getResponsiveConvert().convertTo(object));
        return textContent;
    }
}
